{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0",
   "metadata": {},
   "source": [
    "<a href=\"https://githubtocolab.com/gee-community/geemap/blob/master/docs/notebooks/31_unsupervised_classification.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open in Colab\"/></a>\n",
    "\n",
    "Uncomment the following line to install [geemap](https://geemap.org) if needed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip install geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2",
   "metadata": {},
   "source": [
    "# Machine Learning with Earth Engine - Unsupervised Classification"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3",
   "metadata": {},
   "source": [
    "## Unsupervised classification algorithms available in Earth Engine\n",
    "\n",
    "Source: https://developers.google.com/earth-engine/clustering\n",
    "\n",
    "The `ee.Clusterer` package handles unsupervised classification (or clustering) in Earth Engine. These algorithms are currently based on the algorithms with the same name in [Weka](http://www.cs.waikato.ac.nz/ml/weka/). More details about each Clusterer are available in the reference docs in the Code Editor.\n",
    "\n",
    "Clusterers are used in the same manner as classifiers in Earth Engine. The general workflow for clustering is:\n",
    "\n",
    "1. Assemble features with numeric properties in which to find clusters.\n",
    "2. Instantiate a clusterer. Set its parameters if necessary.\n",
    "3. Train the clusterer using the training data.\n",
    "4. Apply the clusterer to an image or feature collection.\n",
    "5. Label the clusters.\n",
    "\n",
    "The training data is a `FeatureCollection` with properties that will be input to the clusterer. Unlike classifiers, there is no input class value for an `Clusterer`. Like classifiers, the data for the train and apply steps are expected to have the same number of values. When a trained clusterer is applied to an image or table, it assigns an integer cluster ID to each pixel or feature.\n",
    "\n",
    "Here is a simple example of building and using an ee.Clusterer:"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4",
   "metadata": {},
   "source": [
    "![](https://i.imgur.com/IcBapEx.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5",
   "metadata": {},
   "source": [
    "## Step-by-step tutorial\n",
    "\n",
    "### Import libraries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import ee\n",
    "import geemap"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7",
   "metadata": {},
   "source": [
    "### Create an interactive map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8",
   "metadata": {},
   "outputs": [],
   "source": [
    "Map = geemap.Map()\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9",
   "metadata": {},
   "source": [
    "### Add data to the map"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "10",
   "metadata": {},
   "outputs": [],
   "source": [
    "# point = ee.Geometry.Point([-122.4439, 37.7538])\n",
    "point = ee.Geometry.Point([-87.7719, 41.8799])\n",
    "\n",
    "image = (\n",
    "    ee.ImageCollection(\"LANDSAT/LC08/C01/T1_SR\")\n",
    "    .filterBounds(point)\n",
    "    .filterDate(\"2019-01-01\", \"2019-12-31\")\n",
    "    .sort(\"CLOUD_COVER\")\n",
    "    .first()\n",
    "    .select(\"B[1-7]\")\n",
    ")\n",
    "\n",
    "vis_params = {\"min\": 0, \"max\": 3000, \"bands\": [\"B5\", \"B4\", \"B3\"]}\n",
    "\n",
    "Map.centerObject(point, 8)\n",
    "Map.addLayer(image, vis_params, \"Landsat-8\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11",
   "metadata": {},
   "source": [
    "### Check image properties"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12",
   "metadata": {},
   "outputs": [],
   "source": [
    "props = geemap.image_props(image)\n",
    "props.getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13",
   "metadata": {},
   "outputs": [],
   "source": [
    "props.get(\"IMAGE_DATE\").getInfo()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14",
   "metadata": {},
   "outputs": [],
   "source": [
    "props.get(\"CLOUD_COVER\").getInfo()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15",
   "metadata": {},
   "source": [
    "### Make training dataset\n",
    "\n",
    "There are several ways you can create a region for generating the training dataset.\n",
    "\n",
    "- Draw a shape (e.g., rectangle) on the map and the use `region = Map.user_roi`\n",
    "- Define a geometry, such as `region = ee.Geometry.Rectangle([-122.6003, 37.4831, -121.8036, 37.8288])`\n",
    "- Create a buffer zone around a point, such as `region = ee.Geometry.Point([-122.4439, 37.7538]).buffer(10000)`\n",
    "- If you don't define a region, it will use the image footprint by default"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "16",
   "metadata": {},
   "outputs": [],
   "source": [
    "# region = Map.user_roi\n",
    "# region = ee.Geometry.Rectangle([-122.6003, 37.4831, -121.8036, 37.8288])\n",
    "# region = ee.Geometry.Point([-122.4439, 37.7538]).buffer(10000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Make the training dataset.\n",
    "training = image.sample(\n",
    "    **{\n",
    "        #     'region': region,\n",
    "        \"scale\": 30,\n",
    "        \"numPixels\": 5000,\n",
    "        \"seed\": 0,\n",
    "        \"geometries\": True,  # Set this to False to ignore geometries\n",
    "    }\n",
    ")\n",
    "\n",
    "Map.addLayer(training, {}, \"training\", False)\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "18",
   "metadata": {},
   "source": [
    "### Train the clusterer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "19",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Instantiate the clusterer and train it.\n",
    "n_clusters = 5\n",
    "clusterer = ee.Clusterer.wekaKMeans(n_clusters).train(training)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20",
   "metadata": {},
   "source": [
    "### Classify the image"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Cluster the input using the trained clusterer.\n",
    "result = image.cluster(clusterer)\n",
    "\n",
    "# # Display the clusters with random colors.\n",
    "Map.addLayer(result.randomVisualizer(), {}, \"clusters\")\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "22",
   "metadata": {},
   "source": [
    "### Label the clusters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23",
   "metadata": {},
   "outputs": [],
   "source": [
    "legend_keys = [\"One\", \"Two\", \"Three\", \"Four\", \"etc\"]\n",
    "legend_colors = [\"#8DD3C7\", \"#FFFFB3\", \"#BEBADA\", \"#FB8072\", \"#80B1D3\"]\n",
    "\n",
    "# Reclassify the map\n",
    "result = result.remap([0, 1, 2, 3, 4], [1, 2, 3, 4, 5])\n",
    "\n",
    "Map.addLayer(\n",
    "    result, {\"min\": 1, \"max\": 5, \"palette\": legend_colors}, \"Labelled clusters\"\n",
    ")\n",
    "Map.add_legend(\n",
    "    legend_keys=legend_keys, legend_colors=legend_colors, position=\"bottomright\"\n",
    ")\n",
    "Map"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "24",
   "metadata": {},
   "source": [
    "### Visualize the result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "25",
   "metadata": {},
   "outputs": [],
   "source": [
    "print(\"Change layer opacity:\")\n",
    "cluster_layer = Map.layers[-1]\n",
    "cluster_layer.interact(opacity=(0, 1, 0.1))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26",
   "metadata": {},
   "source": [
    "### Export the result\n",
    "\n",
    "Export the result directly to your computer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "out_dir = os.path.join(os.path.expanduser(\"~\"), \"Downloads\")\n",
    "out_file = os.path.join(out_dir, \"cluster.tif\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "28",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_export_image(result, filename=out_file, scale=90)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29",
   "metadata": {},
   "source": [
    "Export the result to Google Drive:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "30",
   "metadata": {},
   "outputs": [],
   "source": [
    "geemap.ee_export_image_to_drive(\n",
    "    result, description=\"clusters\", folder=\"export\", scale=90\n",
    ")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
